001    /*
002     * PairwiseAlignmentFrame.java
003     *
004     * Copyright 2003 Sergio Anibal de Carvalho Junior
005     *
006     * This file is part of NeoBio.
007     *
008     * NeoBio is free software; you can redistribute it and/or modify it under the terms of
009     * the GNU General Public License as published by the Free Software Foundation; either
010     * version 2 of the License, or (at your option) any later version.
011     *
012     * NeoBio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
013     * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
014     * PURPOSE. See the GNU General Public License for more details.
015     *
016     * You should have received a copy of the GNU General Public License along with NeoBio;
017     * if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
018     * Boston, MA 02111-1307, USA.
019     *
020     * Proper attribution of the author as the source of the software would be appreciated.
021     *
022     * Sergio Anibal de Carvalho Junior             mailto:sergioanibaljr@users.sourceforge.net
023     * Department of Computer Science               http://www.dcs.kcl.ac.uk
024     * King's College London, UK                    http://www.kcl.ac.uk
025     *
026     * Please visit http://neobio.sourceforge.net
027     *
028     * This project was supervised by Professor Maxime Crochemore.
029     *
030     */
031    
032    package neobio.gui;
033    
034    import neobio.alignment.*;
035    
036    import java.io.*;
037    import java.awt.*;
038    import java.awt.event.*;
039    import javax.swing.*;
040    import javax.swing.border.*;
041    import javax.swing.event.*;
042    
043    /**
044     * This class is the internal frame of NeoBio's graphical interface for computing pairwise
045     * sequence alignments using one of the the algorithms provided in the {@link
046     * neobio.alignment} package.
047     *
048     * @author Sergio A. de Carvalho Jr.
049     */
050    public class PairwiseAlignmentFrame extends JInternalFrame
051    {
052            private static int              window_number = 1;
053    
054            private Frame                   parent_frame;
055    
056            private JPanel                  input_panel, scoring_panel, algorithm_panel, output_panel;
057            private JPanel                  progress_tab_panel, output_tab_panel;
058            private JTextField              seq1_field, seq2_field, matrix_field, output_field;
059            private JTextField              match_field, mismatch_field, gap_field;
060            private JTextArea               progress_area, output_area;
061            private JButton                 find_seq1_button, find_seq2_button, find_output_button;
062            private JButton                 find_matrix_button, run_button;
063            private JComboBox               algorithm_combo;
064            private JTabbedPane             output_tab;
065            private JRadioButton    screen_button, file_button, basic_button, matrix_button;
066            private ButtonGroup             scoring_group, output_group;
067            private JLabel                  seq1_label, seq2_label;
068            private JLabel                  match_label, mismatch_label, gap_label;
069    
070            private JFileChooser    find_dialog;
071    
072            private boolean                 output_to_file, basic_scheme;
073    
074            private String[]                algorithm_name = {"Needleman & Wunsch (global alignment)",
075                                                                    "Smith & Waterman (local alignment)",
076                                                                    "Crochemore, Landau & Ziv-Ukelson for global alignment",
077                                                                    "Crochemore, Landau & Ziv-Ukelson for local alignment"};
078    
079            private PairwiseAlignmentAlgorithm[]    algorithm = {new NeedlemanWunsch(),
080                                                                    new SmithWaterman(),
081                                                                    new CrochemoreLandauZivUkelsonGlobalAlignment(),
082                                                                    new CrochemoreLandauZivUkelsonLocalAlignment()};
083    
084            /**
085             * Creates a new instance of the internal frame.
086             *
087             * @param parent_frame the parent frame
088             */
089            public PairwiseAlignmentFrame (Frame parent_frame)
090            {
091                    this.parent_frame = parent_frame;
092                    initComponents();
093            }
094    
095            private void initComponents()
096            {
097                    JComponent                      pane;
098                    GridBagConstraints      c;
099    
100                    setIconifiable(true);
101                    setMaximizable(true);
102                    setResizable(true);
103                    setClosable(true);
104                    setTitle("Pairwise Sequence Alignment " + window_number++);
105                    setMinimumSize(new Dimension(500, 500));
106    
107                    pane = (JComponent) getContentPane();
108                    pane.setLayout(new GridBagLayout());
109    
110                    c = new GridBagConstraints();
111                    c.insets = new Insets (4, 4, 4, 4);
112                    c.fill = GridBagConstraints.BOTH;
113                    c.weightx = 1.0; c.weighty = 0;
114    
115                    // input panel
116                    input_panel = new JPanel ();
117                    add (pane, input_panel, c, 0, 0);
118    
119                    // scoring panel
120                    scoring_panel = new JPanel ();
121                    add (pane, scoring_panel, c, 0, 1);
122    
123                    // output panel
124                    output_panel = new JPanel ();
125                    add (pane, output_panel, c, 0, 2);
126    
127                    // algorithm panel
128                    algorithm_panel = new JPanel ();
129                    add (pane, algorithm_panel, c, 0, 3);
130    
131                    c.weightx = 1.0; c.weighty = 1.0;
132    
133                    // output tab
134                    output_tab = new JTabbedPane();
135                    add (pane, output_tab, c, 0, 4);
136    
137                    find_dialog = new JFileChooser();
138                    find_dialog.setDialogTitle("Find...");
139                    find_dialog.setDialogType(JFileChooser.OPEN_DIALOG );
140    
141                    // ***************** INPUT PANEL *****************
142                    input_panel.setLayout(new GridBagLayout());
143                    input_panel.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(
144                                                                                                                    EtchedBorder.LOWERED), "Input"));
145    
146                    seq1_label = new JLabel("Sequence 1:");
147                    seq2_label = new JLabel("Sequence 2:");
148    
149                    seq1_field = new JTextField();
150                    seq1_field.addCaretListener (new CaretListener()
151                            {
152                                    public void caretUpdate (CaretEvent e)
153                                    {
154                                            checkRunButtonStatus ();
155                                    }
156                            });
157    
158                    seq2_field = new JTextField();
159                    seq2_field.addCaretListener (new CaretListener()
160                            {
161                                    public void caretUpdate (CaretEvent e)
162                                    {
163                                            checkRunButtonStatus ();
164                                    }
165                            });
166    
167                    find_seq1_button = new JButton("Find...");
168            find_seq1_button.addActionListener (new ActionListener()
169                            {
170                                    public void actionPerformed (ActionEvent e)
171                                    {
172                                            findSeq1ButtonActionPerformed();
173                                    }
174                            });
175    
176                    find_seq2_button = new JButton("Find...");
177            find_seq2_button.addActionListener (new ActionListener()
178                            {
179                                    public void actionPerformed (ActionEvent e)
180                                    {
181                                            findSeq2ButtonActionPerformed();
182                                    }
183                            });
184    
185                    c.weightx = 0; c.weighty = 0; c.anchor = GridBagConstraints.EAST;
186                    add (input_panel, seq1_label, c, 0, 0);
187                    add (input_panel, seq2_label, c, 0, 1);
188    
189                    c.anchor = GridBagConstraints.CENTER;
190                    add (input_panel, find_seq1_button, c, 2, 0);
191                    add (input_panel, find_seq2_button, c, 2, 1);
192    
193                    c.weightx = 1.0; c.fill = GridBagConstraints.HORIZONTAL;
194                    add (input_panel, seq1_field, c, 1, 0);
195                    add (input_panel, seq2_field, c, 1, 1);
196    
197                    // ***************** SCORING SCHEME PANEL *****************
198                    scoring_panel.setLayout(new GridBagLayout());
199                    scoring_panel.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(
200                                                                                            EtchedBorder.LOWERED), "Scoring Scheme"));
201    
202                    basic_scheme = true;
203                    basic_button = new JRadioButton("Basic:");
204                    basic_button.setSelected(true);
205            basic_button.addItemListener (new ItemListener()
206                            {
207                                    public void itemStateChanged (ItemEvent e)
208                                    {
209                                            schemeOptionStateChanged();
210                                    }
211                            });
212    
213                    matrix_button = new JRadioButton("Substitution Matrix:");
214            matrix_button.addItemListener (new ItemListener()
215                            {
216                                    public void itemStateChanged (ItemEvent e)
217                                    {
218                                            schemeOptionStateChanged();
219                                    }
220                            });
221    
222                    match_label = new JLabel("Match:");
223                    mismatch_label = new JLabel ("Mismatch:");
224                    gap_label = new JLabel ("Gap:");
225    
226                    match_field = new JTextField("1", 2);
227                    match_field.setHorizontalAlignment(JTextField.RIGHT);
228                    match_field.addCaretListener (new CaretListener()
229                            {
230                                    public void caretUpdate (CaretEvent e)
231                                    {
232                                            checkRunButtonStatus ();
233                                    }
234                            });
235    
236                    mismatch_field = new JTextField("-1", 2);
237                    mismatch_field.setHorizontalAlignment(JTextField.RIGHT);
238                    mismatch_field.addCaretListener (new CaretListener()
239                            {
240                                    public void caretUpdate (CaretEvent e)
241                                    {
242                                            checkRunButtonStatus ();
243                                    }
244                            });
245    
246                    gap_field = new JTextField("-1", 2);
247                    gap_field.setHorizontalAlignment(JTextField.RIGHT);
248                    gap_field.addCaretListener (new CaretListener()
249                            {
250                                    public void caretUpdate (CaretEvent e)
251                                    {
252                                            checkRunButtonStatus ();
253                                    }
254                            });
255    
256                    matrix_field = new JTextField();
257                    matrix_field.setEnabled(false);
258                    matrix_field.addCaretListener (new CaretListener()
259                            {
260                                    public void caretUpdate (CaretEvent e)
261                                    {
262                                            checkRunButtonStatus ();
263                                    }
264                            });
265    
266                    find_matrix_button = new JButton("Find...");
267                    find_matrix_button.setEnabled(false);
268            find_matrix_button.addActionListener (new ActionListener()
269                            {
270                                    public void actionPerformed (ActionEvent e)
271                                    {
272                                            findMatrixButtonActionPerformed();
273                                    }
274                            });
275    
276                    scoring_group = new ButtonGroup ();
277                    scoring_group.add(basic_button);
278                    scoring_group.add(matrix_button);
279    
280                    c.weightx = 0; c.fill = GridBagConstraints.NONE;
281                    c.anchor = GridBagConstraints.WEST;
282                    add (scoring_panel, basic_button, c, 0, 0);
283    
284                    c.anchor = GridBagConstraints.EAST;
285                    add (scoring_panel, match_label, c, 1, 0);
286                    add (scoring_panel, mismatch_label, c, 3, 0);
287                    add (scoring_panel, gap_label, c, 5, 0);
288    
289                    c.anchor = GridBagConstraints.WEST;
290                    add (scoring_panel, matrix_button, c, 0, 1);
291    
292                    c.anchor = GridBagConstraints.CENTER;
293                    add (scoring_panel, find_matrix_button, c, 7, 1);
294    
295                    c.weightx = 1.0 / 3; c.fill = GridBagConstraints.HORIZONTAL;
296                    add (scoring_panel, match_field, c, 2, 0);
297                    add (scoring_panel, mismatch_field, c, 4, 0);
298                    add (scoring_panel, gap_field, c, 6, 0);
299    
300                    c.weightx = 1.0; c.gridwidth = 6;
301                    add (scoring_panel, matrix_field, c, 1, 1);
302                    c.gridwidth = 1;
303    
304                    // ***************** OUTPUT PANEL *****************
305                    output_panel.setLayout(new GridBagLayout());
306                    output_panel.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(
307                                                                                                                    EtchedBorder.LOWERED), "Output"));
308    
309                    screen_button = new JRadioButton("Screen");
310                    screen_button.setSelected(true);
311    
312                    output_to_file = false;
313                    file_button = new JRadioButton("File:");
314            file_button.addItemListener (new ItemListener()
315                            {
316                                    public void itemStateChanged (ItemEvent e)
317                                    {
318                                            outputOptionStateChanged();
319                                    }
320                            });
321    
322                    output_field = new JTextField();
323                    output_field.setEnabled(false);
324                    output_field.addCaretListener (new CaretListener()
325                            {
326                                    public void caretUpdate (CaretEvent e)
327                                    {
328                                            checkRunButtonStatus ();
329                                    }
330                            });
331    
332                    find_output_button = new JButton("Find...");
333                    find_output_button.setEnabled(false);
334            find_output_button.addActionListener (new ActionListener()
335                            {
336                                    public void actionPerformed (ActionEvent e)
337                                    {
338                                            findOutputButtonActionPerformed();
339                                    }
340                            });
341    
342                    output_group = new ButtonGroup ();
343                    output_group.add(screen_button);
344                    output_group.add(file_button);
345    
346                    c.weightx = 0; c.weighty = 0; c.fill = GridBagConstraints.NONE;
347                    add (output_panel, screen_button, c, 0, 0);
348                    add (output_panel, file_button, c, 1, 0);
349                    add (output_panel, find_output_button, c, 3, 0);
350    
351                    c.weightx = 1.0; c.weighty = 0; c.fill = GridBagConstraints.HORIZONTAL;
352                    add (output_panel, output_field, c, 2, 0);
353    
354                    // ***************** ALGORITHM PANEL *****************
355                    algorithm_panel.setLayout(new GridBagLayout());
356                    algorithm_panel.setBorder(BorderFactory.createTitledBorder(new EtchedBorder(
357                                                                    EtchedBorder.LOWERED), "Alignment Algorithm"));
358    
359                    algorithm_combo = new JComboBox(algorithm_name);
360    
361                    run_button = new JButton("Run");
362                    run_button.setEnabled(false);
363            run_button.addActionListener (new ActionListener()
364                            {
365                                    public void actionPerformed (ActionEvent e)
366                                    {
367                                            runButtonActionPerformed();
368                                    }
369                            });
370    
371    
372                    c.weightx = 1.0; c.weighty = 0; c.fill = GridBagConstraints.HORIZONTAL;
373                    add (algorithm_panel, algorithm_combo, c, 0, 0);
374    
375                    c.weightx = 0; c.weighty = 0; c.fill = GridBagConstraints.NONE;
376                    add (algorithm_panel, run_button, c, 1, 0);
377    
378                    // ***************** OUTPUT TAB *****************
379                    progress_area = new JTextArea ();
380                    progress_area.setEditable (false);
381                    progress_area.setBorder (BorderFactory.createBevelBorder(BevelBorder.LOWERED));
382                    progress_tab_panel = new JPanel ();
383                    progress_tab_panel.setLayout (new GridLayout());
384                    progress_tab_panel.add (new JScrollPane (progress_area));
385                    output_tab.addTab ("Progress", progress_tab_panel);
386    
387                    output_area = new JTextArea ();
388                    output_area.setEditable (false);
389                    output_area.setBorder (BorderFactory.createBevelBorder(BevelBorder.LOWERED));
390                    output_area.setFont (new Font("Monospaced", Font.PLAIN, 12));
391                    output_tab_panel = new JPanel();
392                    output_tab_panel.setLayout (new GridLayout());
393                    output_tab_panel.add (new JScrollPane (output_area));
394                    output_tab.addTab ("Output", output_tab_panel);
395        }
396    
397        private void add (JComponent a, JComponent b, GridBagConstraints c, int x, int y)
398        {
399                    c.gridx = x;
400                    c.gridy = y;
401                    a.add (b, c);
402            }
403    
404        private void findSeq1ButtonActionPerformed ()
405            {
406                int c = find_dialog.showOpenDialog (this);
407    
408                if (c != JFileChooser.APPROVE_OPTION) return;
409    
410                seq1_field.setText (find_dialog.getSelectedFile().getPath());
411            }
412    
413        private void findSeq2ButtonActionPerformed ()
414            {
415                int c = find_dialog.showOpenDialog (this);
416    
417                if (c != JFileChooser.APPROVE_OPTION) return;
418    
419                seq2_field.setText (find_dialog.getSelectedFile().getPath());
420            }
421    
422        private void findMatrixButtonActionPerformed ()
423            {
424                int c = find_dialog.showOpenDialog (this);
425    
426                if (c != JFileChooser.APPROVE_OPTION) return;
427    
428                matrix_field.setText (find_dialog.getSelectedFile().getPath());
429            }
430    
431        private void findOutputButtonActionPerformed ()
432            {
433                int c = find_dialog.showOpenDialog (this);
434    
435                if (c != JFileChooser.APPROVE_OPTION) return;
436    
437                output_field.setText (find_dialog.getSelectedFile().getPath());
438            }
439    
440            private void schemeOptionStateChanged ()
441            {
442                    basic_scheme = basic_button.isSelected();
443    
444                    match_label.setEnabled(basic_scheme);
445                    match_field.setEnabled(basic_scheme);
446                    mismatch_label.setEnabled(basic_scheme);
447                    mismatch_field.setEnabled(basic_scheme);
448                    gap_label.setEnabled(basic_scheme);
449                    gap_field.setEnabled(basic_scheme);
450    
451                    matrix_field.setEnabled (!basic_scheme);
452                    find_matrix_button.setEnabled (!basic_scheme);
453    
454                    checkRunButtonStatus();
455            }
456    
457            private void outputOptionStateChanged ()
458            {
459                    output_to_file = file_button.isSelected();
460    
461                    output_field.setEnabled (output_to_file);
462                    find_output_button.setEnabled (output_to_file);
463    
464                    checkRunButtonStatus();
465            }
466    
467            private void checkRunButtonStatus ()
468            {
469                    boolean run = true;
470    
471                    if (seq1_field.getText().length() == 0 || seq2_field.getText().length() == 0)
472                    {
473                            run = false;
474                    }
475                    else
476                    {
477                            if (file_button.isSelected() && output_field.getText().length() == 0)
478                            {
479                                    run = false;
480                            }
481                            else
482                            {
483                                    if (matrix_button.isSelected())
484                                    {
485                                            if (matrix_field.getText().length() == 0)
486                                            {
487                                                    run = false;
488                                            }
489                                    }
490                                    else
491                                    {
492                                            if (match_field.getText().length() == 0
493                                                    || mismatch_field.getText().length() == 0
494                                                    || gap_field.getText().length() == 0)
495                                            {
496                                                    run = false;
497                                            }
498                                    }
499                            }
500                    }
501    
502                    if ((run_button.isEnabled() && !run) || (!run_button.isEnabled() && run))
503                            run_button.setEnabled(run);
504            }
505    
506            private void runButtonActionPerformed ()
507            {
508                    ScoringScheme           scoring;
509                    PairwiseAlignment       alignment;
510                    FileReader                      seq1_file, seq2_file, matrix_file;
511                    BufferedWriter          output_file;
512                    String                          seq1_filename, seq2_filename;
513                    String                          matrix_filename, output_filename, message;
514                    int                                     alg, match, mismatch, gap;
515                    long                            start, elapsed;
516    
517                    alg = algorithm_combo.getSelectedIndex();
518    
519                    output_tab.setSelectedIndex(0);
520    
521                    output_area.setText ("");
522    
523                    // ***************** SET SCORING SCHEME *****************
524                    if (basic_scheme)
525                    {
526                            progress_area.setText ("Creating scoring scheme... ");
527                            try
528                            {
529                                    match = Integer.parseInt (match_field.getText());
530                                    mismatch = Integer.parseInt (mismatch_field.getText());
531                                    gap = Integer.parseInt (gap_field.getText());
532    
533                                    scoring = new BasicScoringScheme (match, mismatch, gap);
534                                    algorithm[alg].setScoringScheme(scoring);
535                                    progress_area.append ("OK");
536                            }
537                            catch (NumberFormatException e)
538                            {
539                                    message = "Invalid scoring arguments.";
540                                    progress_area.append ("\n" + message);
541                                    showError (message);
542                                    return;
543                            }
544                    }
545                    else
546                    {
547                            matrix_filename = matrix_field.getText  ();
548                            progress_area.setText ("Loading matrix file... ");
549                            try
550                            {
551                                    matrix_file = new FileReader (matrix_filename);
552                            }
553                            catch (FileNotFoundException e)
554                            {
555                                    message = "File \"" + matrix_filename + "\" not found.";
556                                    progress_area.append("\n" + message);
557                                    showError (message);
558                                    return;
559                            }
560    
561                            try
562                            {
563                                    try
564                                    {
565                                            scoring = new ScoringMatrix (matrix_file);
566                                            algorithm[alg].setScoringScheme(scoring);
567                                            progress_area.append ("OK");
568                                    }
569                                    catch (InvalidScoringMatrixException e)
570                                    {
571                                            matrix_file.close();
572                                            message = "Invalid matrix file \"" + matrix_filename + "\".";
573                                            progress_area.append ("\n" + message);
574                                            showError (message);
575                                            return;
576                                    }
577                                    matrix_file.close();
578                            }
579                            catch (IOException e)
580                            {
581                                    message = "Error reading file.";
582                                    progress_area.append("\n" + message);
583                                    showError (message);
584                                    return;
585                            }
586                    }
587    
588                    // ***************** LOAD SEQUENCES *****************
589                    progress_area.append ("\n\nLoading sequences... ");
590    
591                    seq1_filename = seq1_field.getText ();
592                    try
593                    {
594                            seq1_file = new FileReader (seq1_filename);
595                    }
596                    catch (FileNotFoundException e)
597                    {
598                            message = "File \"" +  seq1_filename +"\" not found.";
599                            progress_area.append("\n" + message);
600                            showError (message);
601                            return;
602                    }
603    
604                    seq2_filename = seq2_field.getText ();
605                    try
606                    {
607                            seq2_file = new FileReader (seq2_filename);
608                    }
609                    catch (FileNotFoundException e)
610                    {
611                            message = "File \"" +  seq2_filename +"\" not found.";
612                            progress_area.append("\n" + message);
613                            showError (message);
614                            return;
615                    }
616    
617                    try
618                    {
619                            try
620                            {
621                                    start = System.currentTimeMillis();
622                                    algorithm[alg].loadSequences (seq1_file, seq2_file);
623                                    elapsed = System.currentTimeMillis() - start;
624                                    progress_area.append ("OK");
625                                    progress_area.append ("\n[ Elapsed time: " + elapsed + " milliseconds ]");
626    
627                            }
628                            catch (InvalidSequenceException e)
629                            {
630                                    seq1_file.close();
631                                    seq2_file.close();
632                                    message = "Invalid sequence files.";
633                                    progress_area.append ("\n" + message);
634                                    showError (message);
635                                    return;
636                            }
637                            seq1_file.close();
638                            seq2_file.close();
639                    }
640                    catch (IOException e)
641                    {
642                            message = "Error reading sequence files.";
643                            progress_area.append("\n" + message);
644                            showError (message);
645                            return;
646                    }
647    
648                    // ***************** EXECUTE ALGORITHM *****************
649                    progress_area.append("\n\nRunning " + algorithm_combo.getSelectedItem() + "... ");
650                    try
651                    {
652                            start = System.currentTimeMillis();
653                            alignment = algorithm[alg].getPairwiseAlignment();
654                            elapsed = System.currentTimeMillis() - start;
655                            progress_area.append ("OK");
656                            progress_area.append ("\n[ Elapsed time: " + elapsed + " milliseconds ]");
657                    }
658                    catch (IncompatibleScoringSchemeException e)
659                    {
660                            message = "Scoring matrix is not compatible with loaded sequences.";
661                            progress_area.append ("\n" + message);
662                            showError (message);
663                            return;
664                    }
665                    catch (OutOfMemoryError e)
666                    {
667                            message = "Insufficient memory to compute an alignment";
668                            progress_area.append ("\n" + message);
669                            showError (message);
670                            return;
671                    }
672    
673                    // ***************** DISPLAY / SAVE OUTPUT *****************
674                    if (output_to_file)
675                    {
676                            output_filename = output_field.getText ();
677                            progress_area.append ("\n\nSaving alignment... ");
678                            try
679                            {
680                                    int length = alignment.getGappedSequence1().length();
681    
682                                    output_file = new BufferedWriter(new FileWriter (output_filename));
683                                    output_file.write(alignment.getGappedSequence1(), 0, length);
684                                    output_file.newLine();
685                                    output_file.write(alignment.getScoreTagLine(), 0, length);
686                                    output_file.newLine();
687                                    output_file.write(alignment.getGappedSequence2(), 0, length);
688                                    output_file.newLine();
689                                    String tmp = "Score: " + alignment.getScore();
690                                    output_file.write(tmp, 0, tmp.length());
691                                    output_file.close();
692                            }
693                            catch (IOException e)
694                            {
695                                    message = "Error writing file \"" + output_filename +"\".";
696                                    progress_area.append("\n" + message);
697                                    showError (message);
698                                    return;
699                            }
700                            progress_area.append ("OK");
701                    }
702                    else
703                    {
704                            output_area.setText (alignment.toString());
705                            output_tab.setSelectedIndex(1);
706                    }
707            }
708    
709            private void showError (String message)
710            {
711                    JOptionPane.showMessageDialog(this, message, "Error", JOptionPane.ERROR_MESSAGE);
712            }
713    }